home *** CD-ROM | disk | FTP | other *** search
- /* ARITH.C
- ************************************************************************
- * *
- * PC Scheme/Geneva 4.00 Borland C code *
- * *
- * (c) 1985-1988 by Texas Instruments, Inc. See COPYRIGHT.TXT *
- * (c) 1992 by L. Bartholdi & M. Vuilleumier, University of Geneva *
- * *
- *----------------------------------------------------------------------*
- * *
- * Basic Arithmetic (+*-/) *
- * *
- *----------------------------------------------------------------------*
- * *
- * Created by: John Jensen Date: 1985 *
- * Revision history: *
- * - 18 Jun 92: Renaissance (Borland Compilers, ...) *
- * - 21 Jan 93: Corrected bug in fixflo (killed flosiz) (lb) *
- * *
- * ``In nomine omnipotentii dei'' *
- ************************************************************************/
-
- #include "mysignal.h"
- #include <float.h>
- #include <stdlib.h>
- #include <string.h>
- #include <math.h>
- #include "scheme.h"
-
- typedef enum { FIX, BIG, FLO }
- NUMBERTAG;
-
- typedef struct {
- NUMBERTAG tag;
- union {
- double flo;
- int fix;
- struct {
- unsigned size;
- BIGDATA *big;
- } B;
- };
- } NUMBER;
-
- #define ABSSMALLER 0x01
- #define ABSGREATER 0x02
- #define SMALLER 0x04
- #define GREATER 0x08
- #define SAMESIGN 0x10
-
- /************************************************************************/
- /* Support of unary arithmetic operations on values other */
- /* than fixnums. */
- /************************************************************************/
- int arith1( int op, REGPTR reg )
- {
- switch (ptype[CORRPAGE(reg->page)])
- {
- case FLOTYPE:
- {
- double flo = reg2c(reg)->flonum.data;
- switch( op )
- {
- case MINUS_OP:
- flo = -flo;
- break;
- case ZERO_OP:
- return flo == 0.0;
- case NEG_OP:
- return flo < 0.0;
- case POS_OP:
- return flo > 0.0;
- case ABS_OP:
- if( flo >= 0.0 )
- return 0;
- else
- flo = -flo;
- break;
- }
- alloc_flonum( reg, flo );
- return 0;
- }
- case BIGTYPE:
- {
- BIGDATA far *big = ®2c(reg)->bignum.data;
-
- switch( op )
- {
- case ZERO_OP:
- return FALSE;
- case POS_OP:
- return !(big->sign & 1);
- case NEG_OP:
- return big->sign & 1;
- case ABS_OP:
- case MINUS_OP:
- BIGDATA *newbig;
-
- if (!(newbig = (BIGDATA *) malloc(big->len+2)))
- {
- errmsg( HEAPERR );
- scheme_error();
- }
- copybig( CORRPAGE(reg->page), reg->disp, newbig );
- if( op == ABS_OP )
- newbig->sign &= 0xfe;
- else newbig->sign ^= 1;
- alloc_int( reg, newbig );
- free( newbig );
- return 0;
- }
- }
- default:
- not_number( op, reg, &nil_reg );
- return -1;
- }
- }
-
- int getnumber( REGPTR reg, NUMBER &number )
- {
- SCHEMEOBJ o = reg2c(reg);
- unsigned type = gettype(reg);
-
- switch( type )
- {
- case FIXTYPE:
- number.tag = FIX;
- number.fix = reg->disp;
- break;
- case FLOTYPE:
- number.tag = FLO;
- number.flo = o->flonum.data;
- break;
- case BIGTYPE:
- number.tag = BIG;
- number.B.size = o->bignum.data.len + 4;
- if( !(number.B.big = (BIGDATA *) malloc(number.B.size)) )
- {
- errmsg( HEAPERR );
- scheme_error();
- }
- copybig( CORRPAGE(reg->page), reg->disp, number.B.big );
- break;
- default:
- return 1;
- }
- return 0;
- }
-
- void convertnumber( NUMBER &number, NUMBERTAG newtag, REGPTR reg )
- {
- if( number.tag == FIX && newtag == FLO )
- number.flo = number.fix;
- else if( number.tag == BIG && newtag == FLO )
- {
- double d;
-
- if( big2flo( number.B.big, &d ) )
- {
- free( number.B.big );
- dos_error( 1, FLONUM_OVERFLOW_ERROR, reg );
- }
- free( number.B.big );
- number.flo = d;
- } else if( number.tag == FIX && newtag == BIG )
- {
- int fix = number.fix;
-
- number.B.size = 7;
- if( !(number.B.big = (BIGDATA *) malloc(number.B.size)) )
- {
- errmsg( HEAPERR );
- scheme_error();
- }
- fix2big( fix, number.B.big );
- }
- number.tag = newtag;
- }
-
- void dological( BIGDATA *dest, BIGDATA *src, int op ) /* dest > op */
- {
- for( int i = 0; i < src->len; i++ )
- switch( op )
- {
- case AND_OP:
- dest->data[i] &= src->data[i];
- break;
- case OR_OP:
- dest->data[i] |= src->data[i];
- break;
- case XOR_OP:
- dest->data[i] ^= src->data[i];
- break;
- }
- if( op == AND_OP )
- for( i = src->len; i < dest->len; i++ )
- dest->data[i] = 0;
- }
-
- /************************************************************************/
- /* Support of binary arithmetic operations on values other */
- /* than fixnums (+, -, *, /, mod) */
- /************************************************************************/
- int arith2( int op, REGPTR reg1, REGPTR reg2 )
- {
- NUMBER number1, number2;
-
- if( getnumber( reg1, number1 ) || getnumber( reg2, number2 ) )
- {
- not_number( op, reg1, reg2 );
- return -1;
- }
- if( number1.tag < number2.tag )
- convertnumber( number1, number2.tag, reg1 );
- else if( number1.tag > number2.tag )
- convertnumber( number2, number1.tag, reg2 );
- /* Perform the operation */
- if( number1.tag == FLO )
- {
- switch( op )
- {
- case ADD_OP:
- number1.flo += number2.flo; break;
- case SUB_OP:
- number1.flo -= number2.flo; break;
- case MUL_OP:
- number1.flo *= number2.flo; break;
- case DIV_OP:
- number1.flo /= number2.flo; break;
- case QUOT_OP:
- set_src_error("QUOTIENT", 2, reg1, reg2 );
- scheme_error();
- case REM_OP:
- number1.flo = fmod( number1.flo, number2.flo );
- break;
- case DIVIDE_OP:
- set_src_error("DIVIDE", 2, reg1, reg2 );
- scheme_error();
- case MOD_OP:
- {
- double t = fmod( number1.flo, number2.flo );
- if( (number1.flo < 0 ^ number2.flo < 0) && t != 0 )
- number1.flo = t + number2.flo;
- else number1.flo = t;
- break;
- }
- case AND_OP:
- set_src_error("LOGAND", 2, reg1, reg2 );
- scheme_error();
- case OR_OP:
- set_src_error("LOGIOR", 2, reg1, reg2 );
- scheme_error();
- case XOR_OP:
- set_src_error("LOGXOR", 2, reg1, reg2 );
- scheme_error();
- case EQ_OP:
- return number1.flo == number2.flo;
- case NE_OP:
- return number1.flo != number2.flo;
- case LT_OP:
- return number1.flo < number2.flo;
- case GT_OP:
- return number1.flo > number2.flo;
- case LE_OP:
- return number1.flo <= number2.flo;
- case GE_OP:
- return number1.flo >= number2.flo;
- }
- alloc_flonum( reg1, number1.flo );
- }
- else { /* then it's BIGNUMs */
- int mag = magcomp( number1.B.big, number2.B.big ) & 0x00ff;
- NUMBER result;
-
- switch( op )
- {
- case SUB_OP:
- number2.B.big->sign ^= 1; /* Negate & fall thru */
- mag ^= SAMESIGN;
- case ADD_OP:
- if( mag & SAMESIGN )
- if( mag & ABSGREATER )
- {
- bigadd( number2.B.big, number1.B.big );
- alloc_int( reg1, number1.B.big );
- } else {
- bigadd( number1.B.big, number2.B.big );
- alloc_int( reg1, number2.B.big );
- }
- else {
- if( mag & ABSGREATER )
- {
- bigsub( number2.B.big, number1.B.big );
- alloc_int( reg1, number1.B.big );
- } else {
- bigsub( number1.B.big, number2.B.big );
- alloc_int( reg1, number2.B.big );
- }
- }
- break;
- case MUL_OP: /* if zero, we're done */
- if( (number1.B.big->len == 1 && !number1.B.big->data[0])
- || (number2.B.big->len == 1 && !number2.B.big->data[0]) )
- {
- alloc_fixnum( reg1, 0 );
- break;
- }
- result.B.size = number1.B.size + number2.B.size - 3;
- if( !(result.B.big = (BIGDATA *) malloc(result.B.size)) )
- {
- free( number1.B.big );
- free( number2.B.big );
- errmsg( HEAPERR );
- scheme_error();
- }
- bigmul( number1.B.big, number2.B.big, result.B.big );
- alloc_int( reg1, result.B.big );
- free( result.B.big );
- break;
- case DIV_OP:
- case QUOT_OP:
- case REM_OP:
- case DIVIDE_OP:
- case MOD_OP:
- if( mag & ABSSMALLER )
- {
- switch( op )
- {
- case DIV_OP: goto float_it;
- case QUOT_OP:
- case DIVIDE_OP: alloc_fixnum( reg1, 0 );
- default: return 0; /* rem is ok */
- }
- }
- result.B.size = number1.B.size - number2.B.size + 7;
- /* at least len, sign & 2 words mantissa for bigdiv */
- if( !(result.B.big = (BIGDATA *) malloc(result.B.size)) )
- {
- free( number1.B.big );
- free( number2.B.big );
- errmsg( HEAPERR );
- scheme_error();
- }
- if ( number1.B.big->data[ number1.B.big->len - 1 ] & 0x8000 )
- {
- number1.B.size += 2;
- number1.B.big = (BIGDATA *) realloc(number1.B.big, number1.B.size);
- number1.B.big->data[ number1.B.big->len++ ] = 0;
- }
- if( bigdiv( number1.B.big, number2.B.big, result.B.big ) )
- {
- free( number1.B.big );
- free( number2.B.big );
- free( result.B.big );
- set_numeric_error( 1, ZERO_DIVIDE_ERROR, reg1 );
- scheme_error();
- }
- if( op == DIV_OP && (number1.B.big->len > 1 || number1.B.big->data[0]) )
- /* test for fractional result */
- {
- free( result.B.big );
- float_it:
- free( number1.B.big ); /* drop the remainder */
- free( number2.B.big ); /* anyway it was trashed */
- getnumber( reg1, number1 );
- getnumber( reg2, number2 );
- convertnumber( number1, FLO, reg1 );
- convertnumber( number2, FLO, reg2 );
- alloc_flonum( reg1, number1.flo / number2.flo );
- return 0;
- }
- switch( op )
- {
- case DIVIDE_OP:
- if( !(mag & SAMESIGN) && (number1.B.big->len > 1 || number1.B.big->data[0]) )
- {
- char mone[7];
- fix2big( -1, (BIGDATA *) mone );
- (magcomp( result.B.big, (BIGDATA *) mone ) & SAMESIGN ?
- bigadd : bigsub)( (BIGDATA *) mone, result.B.big );
- }
- case QUOT_OP:
- case DIV_OP:
- alloc_int( reg1, result.B.big );
- break;
- case MOD_OP:
- if( !(mag & SAMESIGN) && (number1.B.big->len > 1 || number1.B.big->data[0]) )
- {
- free( number2.B.big );
- getnumber( reg2, number2 );
- convertnumber( number2, BIG, reg2 );
- (magcomp( number1.B.big, number2.B.big ) & SAMESIGN ?
- bigadd : bigsub)( number1.B.big, number2.B.big );
- alloc_int( reg1, number2.B.big );
- break;
- }
- case REM_OP:
- alloc_int( reg1, number1.B.big );
- break;
- }
- free( result.B.big );
- break;
- case AND_OP:
- case OR_OP:
- case XOR_OP:
- if( mag & ABSGREATER )
- {
- dological( number1.B.big, number2.B.big, op );
- alloc_int( reg1, number1.B.big );
- } else {
- dological( number2.B.big, number1.B.big, op );
- alloc_int( reg1, number2.B.big );
- }
- break;
- case EQ_OP:
- case NE_OP:
- case LT_OP:
- case GT_OP:
- case LE_OP:
- case GE_OP:
- free( number1.B.big );
- free( number2.B.big );
- switch( op )
- {
- case EQ_OP:
- return !(mag & (ABSSMALLER | ABSGREATER | SMALLER | GREATER));
- case NE_OP:
- return mag & (ABSSMALLER | ABSGREATER | SMALLER | GREATER);
- case LT_OP:
- return mag & SMALLER;
- case GT_OP:
- return mag & GREATER;
- case LE_OP:
- return !(mag & GREATER);
- case GE_OP:
- return !(mag & SMALLER);
- }
- }
- free( number1.B.big );
- free( number2.B.big );
- }
- return 0;
- }
-
- /************************************************************************/
- /* float to integer conversion-- truncate (adjust toward zero) */
- /************************************************************************/
- int atruncate(REGPTR reg)
- {
- switch (ptype[CORRPAGE(reg->page)]) {
- case FLOTYPE:
- {
- double d = reg2c(reg)->flonum.data;
- fixflo( reg, d - fmod( d, 1.0 ) );
- }
- case BIGTYPE: /* bignums and fixnums mutually exclusive */
- case FIXTYPE: /* already a fixnum, so no action required */
- return 0;
- default:
- not_number(TRUNC_OP, reg, &nil_reg); /* invalid type */
- return -1;
- }
- }
-
- /************************************************************************/
- /* float to integer-- floor (adjust toward -infinity) */
- /************************************************************************/
- int afloor(REGPTR reg)
- {
- switch (ptype[CORRPAGE(reg->page)]) {
- case FLOTYPE:
- fixflo( reg, floor( reg2c(reg)->flonum.data ) );
- case BIGTYPE: /* bignums and fixnums mutually exclusive */
- case FIXTYPE: /* already a fixnum, so no action required */
- return 0;
- default:
- not_number(FLOOR_OP, reg, &nil_reg); /* invalid type */
- return -1;
- }
- }
-
- /************************************************************************/
- /* float to integer-- ceiling (adjust toward +infinity) */
- /************************************************************************/
- int aceiling(REGPTR reg)
- {
- switch (ptype[CORRPAGE(reg->page)]) {
- case FLOTYPE:
- fixflo( reg, ceil( reg2c(reg)->flonum.data ) );
- case BIGTYPE: /* bignums and fixnums mutually exclusive */
- case FIXTYPE: /* already a fixnum, so no action required */
- return 0;
- default:
- not_number(CEIL_OP, reg, &nil_reg); /* invalid type */
- return -1;
- }
- }
-
- /************************************************************************/
- /* float to integer-- round (adjust toward nearest integer) */
- /************************************************************************/
- int around(REGPTR reg)
- {
- switch (ptype[CORRPAGE(reg->page)]) {
- case FLOTYPE:
- fixflo( reg, reg2c(reg)->flonum.data ); /* re-allocate as an integer */
- case BIGTYPE: /* bignums and fixnums mutually exclusive */
- case FIXTYPE: /* already a fixnum, so no action required */
- return 0;
- default:
- not_number(ROUND_OP, reg, &nil_reg); /* invalid type */
- return -1;
- }
- }
-
- /************************************************************************/
- /* Convert flonum to integer, which is stored in a register */
- /************************************************************************/
- void fixflo( REGPTR reg, double flo )
- {
- if( fabs(flo) < 0.5 )
- alloc_fixnum( reg, 0 );
- else {
- BIGDATA *bigbuf;
- int size;
-
- frexp( flo, &size );
-
- if( !(bigbuf = (BIGDATA *) malloc( 5 + size/8 )) )
- {
- errmsg(HEAPERR);
- return;
- }
- flotobig( flo, bigbuf );
- alloc_int( reg, bigbuf );
- free( bigbuf );
- }
- }
-
- /************************************************************************/
- /* Convert value to floating point */
- /************************************************************************/
- int sfloat(REGPTR reg)
- {
- NUMBER number;
-
- if( getnumber( reg, number ) )
- {
- not_number(FLOAT_OP, reg, &nil_reg);
- return -1;
- }
-
- convertnumber( number, FLO, reg );
- alloc_flonum( reg, number.flo );
- return 0;
- }
-
- /* What to do when a fixnum result is too large to be fixnum */
- void enlarge(REGPTR reg, long i)
- {
- alloc_block(reg, BIGTYPE, labs(i) > 0xffff ? 5 : 3);
- putlong(reg, i);
- }
-
- /* Arithmetic support error routines */
- /* Arithmetic Operations */
-
- static char *operation[24] = {"+", "-", "*", "/", "REMAINDER",
- "LOGAND", "LOGIOR", "MINUS", "=?", "<>?",
- "<?", ">?", "<=?", ">=?", "ABS",
- "QUOTIENT", "TRUNCATE", "FLOOR", "CEILING", "ROUND",
- "FLOAT", "ZERO?", "POSITIVE?", "NEGATIVE?"};
- /* Note: TRUE -> binary operation; FALSE -> unary operation */
- static char binary[24] = {TRUE, TRUE, TRUE, TRUE, TRUE,
- TRUE, TRUE, FALSE, TRUE, TRUE,
- TRUE, TRUE, TRUE, TRUE, FALSE,
- TRUE, FALSE, FALSE, FALSE, FALSE,
- FALSE, FALSE, FALSE, FALSE};
-
- void not_number(int op, REGPTR reg1, REGPTR reg2)
- {
- tmp_reg = nil_reg;
- if (binary[op])
- cons(&tmp_reg, reg2, &tmp_reg);
- cons(reg1, reg1, &tmp_reg);
- intern(&tmp_reg, operation[op], strlen(operation[op]));
- cons(reg1, &tmp_reg, reg1);
- set_numeric_error(1, NUMERIC_OPERAND_ERROR, reg1);
- reg1->disp = NTN_DISP;
- reg1->page = ADJPAGE(NTN_PAGE);
- }
-
- /************************************************************************/
- /* Put the next number in the present pseudo-random sequence into REG */
- /* For details on the generator KRANDOM, see the file STIMER.ASM */
- /************************************************************************/
- void srandom(REGPTR reg)
- {
- alloc_fixnum( reg, rand() );
- }
-
- /************************************************************************/
- /* What to do in the event of a floating-point exception */
- /************************************************************************/
- #pragma argsused
- void fperror( int sign, int subcode, int *reglist )
- {
- /* first, bump off all arguments from math stack */
- for( int i = 0; i < 8; i++ )
- asm {
- ffree st(0)
- fdecstp
- }
-
- switch ( subcode ) {
- case FPE_OVERFLOW: /* Overflow */
- case FPE_INTOVFLOW:
- set_numeric_error( 1, FLONUM_OVERFLOW_ERROR, &nil_reg );
- break;
- case FPE_ZERODIVIDE: /* Divide by zero */
- case FPE_INTDIV0:
- set_numeric_error( 1, ZERO_DIVIDE_ERROR, &nil_reg );
- break;
- }
- signal( SIGFPE, fperror ); /* restart floating exception handler */
- scheme_error(); /* signal the error to interprt */
- }
-
- #pragma warn -rvl
- int matherr( struct exception *e )
- {
- switch( e->type )
- {
- case DOMAIN:
- case SING:
- case OVERFLOW:
- case UNDERFLOW:
- case TLOSS:
- set_numeric_error( 1, NUMERIC_OPERAND_ERROR, &nil_reg );
- }
- scheme_error();
- }
- #pragma warn +rvl